/*
 * Minimit Gallery Plugin 2.0
 * Copyright (C) 2012 by Riccardo Caroli http://www.minimit.com
 * Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
 * For info and updates visit http://www.minimit.com/works/minimit-gallery-plugin
 */
/*
 * Minimit Gallery constructor
 * @function Mg(settings:object)
 * 	@param settings.reference:string [Required] - gallery reference string
 * 	@param settings.eventName:object [can be multiple] - arguments for event interactions, the name must be the name of a valid Javascript event: click dblclick mousedown mouseup mousemove mouseenter OR mouseover mouseleave OR mouseout touchstart touchend touchcancel touchleave touchmove focus
 *  @param settings.eventName.activated:array [Default:null] - array of numbers for activated items on init
 *  @param settings.eventName.cycle:boolean [Default:false] - if the activation cycles
 *  @param settings.eventName.prevSteps:number [Default:1] - how many to jump on prev
 *  @param settings.eventName.nextSteps:number [Default:1] - how many to jump on next
 *  @param settings.eventName.prevtosteps:boolean [Default:false] - for prev, true if the items scrolls all the way, false if the items scrolls only to show first and last
 *  @param settings.eventName.nexttosteps:boolean [Default:false] - for next, true if the items scrolls all the way, false if the items scrolls only to show first and last
 *  @param settings.eventName.interactive:boolean [Default:false] - if the items are interactive on click
 *  @param settings.eventName.anchorize:boolean [Default:false] - if you want to implement url anchors to remember position
 *  @param settings.eventName.linked:array [Default:null] - object of linked galleries 
 *  @param settings.eventName.linkedmultiply:number [Default:1] - number to multiply the activation of linked galleries
 *  @param settings.eventName.maxActivated:number [Default:1] - maximum amount of activated items - unlimited if Infinity
 *  @param settings.eventName.deactivable:boolean [Default:false] - if the click activated items can be deactivated on click
 *  @param settings.eventName.multiLess:number [Default:0] - how many you want before activated in the multiple data - nothing if undefined
 *  @param settings.eventName.multiPlus:number [Default:0] - how many you want after activated in the multiple data - nothing if undefined
 *  @param settings.eventName.auto:number [Default:null] - milliseconds for automatic gallery
 *  @param settings.eventName.autoSlow:number [Default:null] - milliseconds for automatic gallery pause when user interacts
 *  @param settings.eventName.autoInverse:boolean [Default:false] - false for automatic to next, true for automatic to prev
 */
function Mg(settings) {
	this.events = []; // array of events
    this.setSettings(settings);
	this.refreshItems();
	this.initEvents();
}

/*
 * Minimit Gallery Event you can to specify, can be multiple
 * @function MgObject.eventName.onEvent(isInit)
 * 	@param isInit:boolean - true if it is the init call, false otherwise
 * Besides all constructor eventName variables you have also this below
 * 	@variable this.items:documentElements - the javascript documentElements of the gallery
 * 	@variable this.reference:string - the gallery reference string
 * 	@variable this.activated:array - array of item numbers activated
 * 	@variable this.deactivated:number - item number deactivated
 * 	@variable this.multiActivated:array - array of item numbers activated with multiLess and multiPlus
 * 	@variable this.multiBeforeIn:array -  array of item numbers activated on left with multiLess and multiPlus
 * 	@variable this.multiBeforeOut:array -  array of item numbers deactivated on left with multiLess and multiPlus
 * 	@variable this.multiAfterIn:array -  array of item numbers activated on right with multiLess and multiPlus
 * 	@variable this.multiAfterOut:array -  array of item numbers deactivated on right with multiLess and multiPlus
 * 	@variable this.direction:number - 0 going left, 1 going right, null for no direction
 */
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////// CORE FUNCTIONS //////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/*
 * Set or update gallery settings
 * @function setSettings(settings)
 * 	@param settings:object - all the settings to activate
 */
Mg.prototype.setSettings = function(settings) {
	for(var i in settings){
		if(typeof(settings[i])=="object" && !settings[i].length){
			// store array of events name
			if(this[i]==undefined){this[i] = {};this.events.push(i);}
			// pre-defaults
			if(settings.reference){this[i].reference = settings.reference;}
			this[i].linkedMultiply = 1;
			this[i].prevSteps = 1; this[i].nextSteps = 1;
			// store all events settings
			for(var z in settings[i]){this[i][z] = settings[i][z];}
			// post-default
			if(this[i].activated == undefined){this[i].activated = [];}
			if(this[i].activated.length<1){this[i].activated.push(null);}
		}else{
			// store all other settings
			this[i] = settings[i];
		}
	}
};

/*
 * Refresh the items of the gallery
 * @function refreshItems()
 */
Mg.prototype.refreshItems = function() {
	this.items = this.getElementsById(this.reference+"-item-");
};

/*
 * Initialize the gallery
 * @function init()
 */
Mg.prototype.init = function() {
	for(var i=0;i<this.events.length;i++){
		var evnt = this.events[i];
		var thisEvnt = this[evnt];
		// set nocheck
		if(thisEvnt.prevHide != undefined || thisEvnt.prevShow != undefined || thisEvnt.nextHide != undefined || thisEvnt.nextShow != undefined){
			thisEvnt.noCheck = false;
		}else{
			thisEvnt.noCheck = true;
		}
		// setState for init
		for(var z in thisEvnt.activated){
			this.setState(thisEvnt.activated[z], evnt, false, false, true);
		}
		// start automatic
		if(thisEvnt.auto != undefined){this.autoStart(this.events[i]);}
	}
}

/*
 * Set the gallery state
 * @function setState(num, evnt, alsoLinked, pauseAuto, isInit)
 * 	@param num:number - number to activate
 * 	@param evnt:string - event to change
 * 	@param alsoLinked:boolean - if it propagates to linked
 * 	@param pauseAuto:boolean - if it pauses automatic
 * 	@param isInit:boolean - if it is init
 */
Mg.prototype.setState = function(num, evnt, alsoLinked, pauseAuto, isInit){
	var thisEvnt = this[evnt];
	// anchorize
	if(thisEvnt.anchorize){
		var tnum = this.makeAnchor(num, evnt, isInit);
		if(tnum != undefined){ // if anchor value is found
			num = parseFloat(tnum); // makes num as the anchor
			alsoLinked = true; // cause it will propagate
		}
	}
	// call event
	if(thisEvnt.onEvent != undefined){
		// activation
		thisEvnt.oldNum = thisEvnt.num;
		thisEvnt.num = num;
		if(!isInit || tnum != undefined){
			thisEvnt.deactivated = this.setActivated(num, evnt); // core function that manages activated and deactivated
		}else{
			if(thisEvnt.activated[0]==null){thisEvnt.activated.splice(0, 1);} // fix remove null activated 
		}
		// multiple
		if(thisEvnt.multiPlus+thisEvnt.multiLess > 0){this.setMutiple(evnt, isInit);}
		thisEvnt.onEvent(isInit);
		// others
		if(!thisEvnt.cycle && !thisEvnt.noCheck){this.checkPrevNext(evnt);}
		if(!thisEvnt.deactivable){
			for(var i=0; i<thisEvnt.activated.length; i++){
				this.enableTextSelect(document.getElementById(this.reference+"-item-"+thisEvnt.activated[i]));
			}
			if(thisEvnt.deactivated != undefined){
				this.disableTextSelect(document.getElementById(this.reference+"-item-"+thisEvnt.deactivated));
			}
		}
	}
	// pause automatic
	if(pauseAuto){this.autoSlow(evnt);}
	// propagate to linked
	if(alsoLinked){
		for(var i in thisEvnt.linked){
			thisEvnt.linked[i].setState(num*thisEvnt.linkedMultiply, evnt, false, true, isInit);
		}
	}
};

/*
 * Calculate and return deactivated item, and remove it from activated array
 * @function setActivated(num, evnt)
 * 	@param num:number - number to set
 * 	@param evnt:string - event to change
 */
Mg.prototype.setActivated = function(num, evnt){
	var activated = this[evnt].activated;
	var spliced = null;
	var idx = this.inArray(num, activated);
	if(idx != -1){ // if the num is already on the activated array
		if(this[evnt].deactivable){
			spliced = parseFloat(activated.splice(idx, 1)); // disactivate the activate one
		}
	}else{ // if num wasn't already on the activated array
		if(activated.length >= this[evnt].maxActivated || !this[evnt].maxActivated){ // if activated are more than maxActivated or if maxActivated undefined
			spliced = parseFloat(activated.splice(activated.length-1, 1)); // remove the last
		}
		activated.unshift(num);
	}
	return spliced;
};

/*
 * Return multi data
 * @function setMutiple(evnt, isInit)
 * 	@param evnt:string - event to change
 * 	@param isInit:boolean - if true it doesn't count multiple
 */
Mg.prototype.setMutiple = function(evnt, isInit){
	var thisEvnt = this[evnt];
	var target = thisEvnt.num;
	var oldTarget = thisEvnt.oldNum;
	var length = this.items.length;
	if(target != null || isInit){ // if null we skip this to keep last multi variables
		if(thisEvnt.multiActivated){
			thisEvnt.multiOldActivated = thisEvnt.multiActivated;
		}else{
			thisEvnt.multiOldActivated = this.mapNum(oldTarget, thisEvnt.multiLess, thisEvnt.multiPlus, length, thisEvnt.cycle);
		}
		thisEvnt.multiActivated = this.mapNum(target, thisEvnt.multiLess, thisEvnt.multiPlus, length, thisEvnt.cycle);
		thisEvnt.multiBeforeIn = [];
		thisEvnt.multiBeforeOut = [];
		thisEvnt.multiAfterIn = [];
		thisEvnt.multiAfterOut = [];
		thisEvnt.direction = null;
		if(!isInit){ // we don't need to calculate this if isInit
			var dist = this.findNearestDistance(target, oldTarget, length, thisEvnt.cycle);
			thisEvnt.distance = dist;
			if(dist > 0){ // going right
				thisEvnt.direction = 1;
				for(var i=0; i<Math.abs(dist); i++){
					thisEvnt.multiAfterIn.unshift(thisEvnt.multiActivated[thisEvnt.multiOldActivated.length-i-1]);
					thisEvnt.multiBeforeOut.unshift(thisEvnt.multiOldActivated[dist-i-1]);
				}
				thisEvnt.multiDeactivated = thisEvnt.multiBeforeOut;
			}else{ // going left
				thisEvnt.direction = 0;
				for(var i=0; i<Math.abs(dist); i++){
					thisEvnt.multiBeforeIn.push(thisEvnt.multiActivated[-dist-i-1]);
					thisEvnt.multiAfterOut.push(thisEvnt.multiOldActivated[thisEvnt.multiOldActivated.length-i-1]);
				}
				thisEvnt.multiDeactivated = thisEvnt.multiAfterOut;
			}
		}
	}
};

/*
 * Initialize and attach events on items and prev next first last
 * @function initEvents()
 */
Mg.prototype.initEvents = function(){
	for(var i=0;i<this.events.length;i++){
		// init items events
		var evnt = this.events[i];
		if(this[evnt].interactive){
			for(var z=0;z<this.items.length;z++){
				var item = this.items[z];
				if(evnt == "click"){this.disableTextSelect(item);}
				item.that = this;
				item.num = z;
				item[this.getEventName(evnt)] = evnt;
				// register event
				this.addEvent(item, evnt, function(e){ // register event
					this.that.setState(this.num, this[this.that.getEventName(e.type)], true, true, false);
					if(e.stopPropagation != undefined){e.stopPropagation();}else{e.cancelBubble = true;} // ie7 ie8 fix
					return false;
				}, false);
			}
		}
		// init prev next first last events
		var arr = ["prev","next","first","last"];
		for(var z=0;z<arr.length;z++){
			var item = document.getElementById(this.reference+"-"+evnt+"-"+arr[z]);
			if(item){
				this.disableTextSelect(item);
				item.that = this;
				item.evnt = evnt;
				item.call = arr[z];
				// register event
				this.addEvent(item, "click", function(e){ // register event
					this.that[this.call](this.evnt, true, true);
					if(e.stopPropagation != undefined){e.stopPropagation();}else{e.cancelBubble = true;} // ie7 ie8 fix
					return false;
				}, false);
			}
		}
	}
};

/*
 * Manages show hide events
 * @function checkPrevNext(evnt)
 * 	@param evnt:string - event to watch
 */
Mg.prototype.checkPrevNext = function(evnt){
	var thisEvnt = this[evnt];
	// prev
	var target = thisEvnt.num-thisEvnt.prevSteps;
	if(thisEvnt.prevToSteps){
		if(target < 0-thisEvnt.multiLess-thisEvnt.multiPlus){
			thisEvnt.prevIsShown = false;
			if(thisEvnt.prevHide != undefined){thisEvnt.prevHide();}
		}else if(!thisEvnt.prevIsShown){
			thisEvnt.prevIsShown = true;
			if(thisEvnt.prevShow != undefined){thisEvnt.prevShow();}
		}
	}else{
		if(target<0){
			thisEvnt.prevIsShown = false;;
			if(thisEvnt.prevHide != undefined){thisEvnt.prevHide();}
		}else if(!thisEvnt.prevIsShown){
			thisEvnt.prevIsShown = true;
			if(thisEvnt.prevShow != undefined){thisEvnt.prevShow();}
		}
	}
	// next
	var target = thisEvnt.num+thisEvnt.nextSteps;
	if(thisEvnt.nextToSteps){
		if(target > this.items.length-thisEvnt.multiLess-thisEvnt.multiPlus-1){
			thisEvnt.nextIsShown = false;
			if(thisEvnt.nextHide != undefined){thisEvnt.nextHide();}
		}else if(!thisEvnt.nextIsShown){
			thisEvnt.nextIsShown = true;
			if(thisEvnt.nextShow != undefined){thisEvnt.nextShow();}
		}
	}else{
		if(target>this.items.length-1){
			thisEvnt.nextIsShown = false;
			if(thisEvnt.nextHide != undefined){thisEvnt.nextHide();}
		}else if(!thisEvnt.nextIsShown){
			thisEvnt.nextIsShown = true;
			if(thisEvnt.nextShow != undefined){thisEvnt.nextShow();}
		}
	}
};

/*
 * Set prev
 * @function prev(evnt, alsoLinked, pauseAuto)
 * 	@param evnt:string - event to change
 * 	@param alsoLinked:boolean - if it propagates to linked
 * 	@param pauseAuto:boolean - if it pauses automatic
 */
Mg.prototype.prev = function(evnt, alsoLinked, pauseAuto){
	var thisEvnt = this[evnt];
	var length = this.items.length;
	var target = thisEvnt.num-thisEvnt.prevSteps;
	if(thisEvnt.prevToSteps && !thisEvnt.cycle){
		if(target<0-thisEvnt.multiLess){ // if target is out of prev bounds
			target = 0-thisEvnt.multiLess;
		}
	}else{
		if(target<0){ // if target is out of prev bounds
			if(thisEvnt.cycle){
				target = length-Math.abs(target);
			}else{
				target = 0;
			}
		}
	}
	if(thisEvnt.prevClick != undefined){thisEvnt.prevClick();}
	this.setState(target, evnt, alsoLinked, pauseAuto);
};

/*
 * Set next
 * @function next(evnt, alsoLinked, pauseAuto)
 * 	@param evnt:string - event to change
 * 	@param alsoLinked:boolean - if it propagates to linked
 * 	@param pauseAuto:boolean - if it pauses automatic
 */
Mg.prototype.next = function(evnt, alsoLinked, pauseAuto){
	var thisEvnt = this[evnt];
	var length = this.items.length;
	var target = thisEvnt.num+thisEvnt.nextSteps;
	if(thisEvnt.nextToSteps && !thisEvnt.cycle){
		if(target>length-1+thisEvnt.multiPlus){ // if target is out of next bounds
			target = length-1+thisEvnt.multiPlus;
		}
	}else{
		if(target>length-1){ // if target is out of next bounds
			if(thisEvnt.cycle){
				target = Math.abs(target)-length;
			}else{
				target = thisEvnt.num;
			}
		}
	}
	if(thisEvnt.nextClick != undefined){thisEvnt.nextClick();}
	this.setState(target, evnt, alsoLinked, pauseAuto);
};

/*
 * Set first
 * @function first(evnt)
 * 	@param evnt:string - event to change
 */
Mg.prototype.first = function(evnt){
	this.setState(0, evnt, true, true);
}

/*
 * Set last
 * @function last(evnt)
 * 	@param evnt:string - event to change
 */
Mg.prototype.last = function(evnt){
	this.setState(this.items.length-1, evnt, true, true);
}

/*
 * Starts automatic
 * @function autoStart(evnt)
 * 	@param evnt:string - automatic event to start
 */
Mg.prototype.autoStart = function(evnt){
    var that = this; 
	clearInterval(this[evnt+"Interval"]);
	if(this.autoInverse){
		this[evnt+"Interval"] = setInterval(function(){that.prev(evnt, true, false);}, this[evnt].auto);
	}else{
		this[evnt+"Interval"] = setInterval(function(){that.next(evnt, true, false);}, this[evnt].auto);
	}	
}

/*
 * Slows automatic
 * @function autoSlow(evnt)
 * 	@param evnt:string - automatic event to slow
 */
Mg.prototype.autoSlow = function(evnt){
    var that = this; 
	if(this[evnt].autoSlow != undefined){
		clearInterval(this[evnt+"Interval"]);
		this[evnt+"Interval"] = setInterval(function(){that.autoStart(evnt);}, this[evnt].autoSlow);
	}
}

/*
 * Stops automatic
 * @function autoStop(evnt)
 * 	@param evnt:string - automatic event to stop
 */
Mg.prototype.autoStop = function(evnt){
	clearInterval(this[evnt+"Interval"]);
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////// TOOLS FUNCTIONS //////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/*
 * Makes and manage multiple anchors as key=value
 * @function makeAnchor(value, key, justRead)
 * 	@param value:string - value to store
 * 	@param key:string - key to store
 * 	@param justRead:boolean - if false it saves the anchor, if true it returns the current value on the browser
 */
Mg.prototype.makeAnchor = function(value, key, justRead){
	var anchor = window.location.hash.replace("#", "");
	var arr = anchor.split("&");
	var found;
	for(var i=0;i<arr.length;i++){
		var values = arr[i].split("=");
		if(values[0] == this.reference+key){
			found = i;
			break;
		}
	}
	//
	if(justRead){
		if(found!=undefined){
			return(values[1]);
		}else{
			return(undefined);
		}
	}else{
		if(found!=undefined){
			arr[found] = this.reference+key+"="+value;
		}else{
			arr.push(this.reference+key+"="+value);
		}
		//
		var output = "#";
		for(var i=0;i<arr.length;i++){
			if(arr[i]!=""){
				output += arr[i];
				if(i!=arr.length-1){
					output += "&";
				}
			}
		}
		window.location.hash = output;
		return(value);
	}
};

/*
 * Add Javascript events: support mouseenter and mouseleave and fix scope of event handlers 
 * @function addEvent(obj, evnt, fn, useCapture)
 * 	@param obj:documentElement - a single element in a document
 * 	@param evnt:string - event name
 * 	@param fn:function - function to call on event
 * 	@param useCapture:boolean - useCapture indicates if the user wishes to initiate capture
 */
Mg.prototype.addEvent = function(obj, evnt, fn, useCapture){
	if(typeof obj.attachEvent != 'undefined'){ // ie7 ie8 fix
		// utils for attachEvent
		function getEvent(e, boundEl){
			var ev = e || window.event;
		    if (!ev) {
		        var c = this.getEvent.caller;
		        while (c) {
		            ev = c.arguments[0];
		            if (ev && Event == ev.constructor) {
		                break;
		            }
		            c = c.caller;
		        }
		    }
		    return ev;
		}
		var wrappedFn = function(e) {
	        return fn.call(obj, getEvent(e, obj));
	    };
		// core for attachEvent
		obj.attachEvent("on"+evnt, wrappedFn);
	}else{
		// utils for addEventListener
		function mouseEnter(fn){
			return function(evnt){
				var relTarget = evnt.relatedTarget;
				if(this == relTarget || isAChildOf(this, relTarget)){return;}
				fn.call(obj, evnt);
			}
		};
		function isAChildOf(parent, child){
			if(parent == child){return false;}
			while(child && child !== parent){child = child.parentNode;}
			return child == parent;
		};
		// core for addEventListener
		if(evnt == 'mouseenter'){
			obj.addEventListener('mouseover', mouseEnter(fn), useCapture);
		}else if(evnt == 'mouseleave'){
			obj.addEventListener('mouseout', mouseEnter(fn), useCapture);
		}else{
			obj.addEventListener(evnt, fn, useCapture);
		}
	}
};

/*
 * Fix for mouseenter and mouseleave
 * @function getEventName(evnt)
 * 	@param evnt:string - event to replace
 */
Mg.prototype.getEventName = function(evnt){
	if(evnt == 'mouseenter'){
		return 'mouseover';
	}else if(evnt == 'mouseleave'){
		return 'mouseout';
	}else{
		return evnt;
	}
}

/*
 * Returns if needle is in haystack
 * @function inArray(needle, haystack)
 * 	@param needle - what to search
 * 	@param haystack:array - array to search
 */
Mg.prototype.inArray = function(needle, haystack) {
	if(!haystack){return -1}
    for(var i=0;i<haystack.length;i++) {
        if(haystack[i] == needle) return i;
    }
    return -1;
};

/*
 * Returns multiple elements by id attribute
 * @function getElementsById(id)
 * 	@param id:string - id attribute
 */
Mg.prototype.getElementsById = function(id) {
	var children = document.body.getElementsByTagName('*');
	var elements = [],child;
	for(var i = 0, length = children.length; i < length; i++) {
		child = children[i];
		if(child.id.substr(0,id.length) == id)
			elements.push(child);
	}
	return elements;
};

/*
 * Enable text selection
 * @function enableTextSelect(obj)
 * 	@param obj:documentElement - a single element to enable text
 */
Mg.prototype.enableTextSelect = function(obj){
	if(obj){
		obj.style.MozUserSelect = "text";
		obj.onselectstart = function(){};
	}
};

/*
 * Disable text selection
 * @function disableTextSelect(obj)
 * 	@param obj:documentElement - a single element to disable text
 */
Mg.prototype.disableTextSelect = function(obj){
	if(obj){
		obj.style.MozUserSelect = "none";
		obj.onselectstart = function(){return false;};
	}
};

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////// UTILS FUNCTIONS //////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/*
 * Returns a maps a serie of numbers based on num +- add, between 0 and max (ex: 4, 5, 0, 1, 2)
 * @function mapnum(num, less, plus, max, cycle)
 * 	@param num:number - number
 * 	@param less:number - additional before the num
 * 	@param plus:number - additional after the num
 *	@param max:number - maximum number
 *	@param cycle:boolean - if the values cycle, from max to 0 and vice versa
 */
Mg.prototype.mapNum = function(num, less, plus, max, cycle){
	var arr = [];
	for(var i=-less;i<=plus;i++){
		if(num+i<max || cycle){ // here if num>max we put null because the item doesn't exist, except with cycle
			if(num+i<0 && cycle){
				arr.push(max-(-num-i));
			}else if(num+i>=max && cycle){
				arr.push(num+i-max);
			}else{
				arr.push(num+i);
			}
		}else{
			arr.push(null);
		}
	}
	return arr;
};

/*
 * Returns the distance between num and center, with center having the lowest number, based on max and min (ex: 2,1,0,1,2)
 * @function mapDistance(center, num)
 * 	@param center:number - to number
 * 	@param num:number - from number
 */
Mg.prototype.mapDistance = function(center, num){
	return center-num;
};

/*
 * Returns the reverse distance between num and center, with center having the highest number, based on max and min (ex: 0,1,2,1,0)
 * @function mapDistanceReverse(center, num, max, min)
 * 	@param center:number - to number
 * 	@param num:number - from number
 *	@param max:number - max number
 * 	@param min:number - min number
 */
Mg.prototype.mapDistanceReverse = function(center, num, max, min){
	var max_distance = max-min;
	if (num == center){
		return center;
	}else if(num<max_distance/2){
		return -num;
	}else if(num>max_distance/2){
		return (max_distance-num-1);
	}else{
		return max_distance/2;
	}
};

/*
 * Returns the nearest distance between i and z, considering values from 0 to max
 * The distance evaluate the nearest considering cycling from 0 to max if cycle is true
 * @function findNearestDistance(i, z, max, cycle)
 * 	@param i:number - from number
 * 	@param z:number - to number
 *	@param max:number - max number of values
 *	@param cycle:boolean - if the values cycle, from max to 0 and vice versa
 */
Mg.prototype.findNearestDistance = function(i, z, max, cycle){
	var maxDist = max/2;
	if(!cycle || (i-z<maxDist && i-z>-maxDist)){ // not cycle or into the max distance limits
		return (i-z);
	}else if(i-z>maxDist){ // if we are going right over the limits
		if(z>i){
			return (-i-(max-z));
		}else{
			return (-z-(max-i));
		}
	}else if(i-z<-maxDist){ // if we are going left over the limits
		if(z>i){
			return (i+(max-z));
		}else{
			return (z+(max-i));
		}
	}
	return 0;
};
